package coop.bancocredicoop.proyectos.gd.fileListing;

import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Comparator;

import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import coop.bancocredicoop.proyectos.gd.DocumentFile;
import coop.bancocredicoop.proyectos.gd.DocumentStream;
import coop.bancocredicoop.proyectos.gd.SourceDocument;
import coop.bancocredicoop.proyectos.gd.fileListing.FolderItem;
import coop.bancocredicoop.proyectos.gd.fileListing.InvalidFolderException;

public class DocumentBrowser {

	private static final Log log = LogFactory.getLog(DocumentBrowser.class);
	private String rootPath;
	private File rootFolder;
	
	public DocumentBrowser(String rootPath) throws IOException {
		rootFolder = new File(rootPath); 
		this.rootPath = rootFolder.getCanonicalPath();
		
		if (!rootFolder.isDirectory()) {
			throw new IllegalArgumentException("rootFolder no es un directorio: " + rootPath);
		}
				
	}
	
	public List<DirectoryItem> getListDirectory(File baseFolder) throws IOException, InvalidFolderException {
		
		String canonicalPath = baseFolder.getCanonicalPath();
		File folder = new File(canonicalPath);
		
		if (!folder.isDirectory()) {
			throw new IllegalArgumentException("baseFolder no es un directorio: " + canonicalPath);
		}
		
		validatePath(canonicalPath);
		
		File[] arrayFile = folder.listFiles();
		List<DirectoryItem> documentList = new LinkedList<DirectoryItem>();

		for (File file : arrayFile) {
			if (file.isDirectory()) {
				FolderItem folderItem = new FolderItem(file, this);
				documentList.add(folderItem);
			} else if (isDocument(file)) {
				documentList.add(new FileItem(file, this));
			} else {
				log.info(String.format("El archivo no es un documento ni una carpeta: %s", file.getName()));
			}
		}
		
		Collections.sort(documentList, new Comparator<DirectoryItem>() {
	        
	        public int compare(DirectoryItem item1, DirectoryItem item2)
	        {
	        	// directories first
	        	if (item1.getIsDirectory() && !item2.getIsDirectory()) {
	        		return -1;
	        	} else if (item2.getIsDirectory() && !item1.getIsDirectory()) {
	        		return 1;
	        	} else {
	        		return item1.getName().compareTo(item2.getName());
	        	}
	        }
	    }); 
		
		return documentList;
				
	}

	public void validatePath(String canonicalPath) throws InvalidFolderException {
		if (!canonicalPath.startsWith(rootPath)) {
			throw new InvalidFolderException("La carpeta se encuentra fuera de la raiz: " + canonicalPath);
		}
	}
	
	public Integer countDocuments(Integer counter, File file) throws IOException {
			
		if (isDocument(file)) {
			return 1;
		} else if (file.isDirectory()) {
			for (File dirItem : file.listFiles()) {
				counter += countDocuments(0, dirItem);
			}
			return counter;
		} else {
			return counter;
		}
			
	}
	
	public Integer getAmountOfDocuments() throws IOException {
		return countDocuments(0, rootFolder);
	}
	
	private boolean isDocument(File file) {
		return  file.isFile() &&
				file.getName().length() > 3 &&
				file.getName().substring(file.getName().length() - 4)
				.toLowerCase()
				.equals(".pdf");
	}

	public String getRelativePath(File file) throws IOException, InvalidFolderException {
		String cannonicalPath = file.getCanonicalPath();
		validatePath(cannonicalPath);
		if (isRootDirectory(cannonicalPath)) {
			return "";
		} else {
			return cannonicalPath.substring(rootPath.length() + 1);
		}
	}

	public boolean isRootDirectory(String canonicalPath) throws IOException {
		return rootFolder.getCanonicalPath().equals(canonicalPath);
	}

	public String getParentDirectory(File folder) throws InvalidFolderException, IOException {
		File parentFile = folder.getParentFile();
		return getRelativePath(parentFile);
	}

	public void documentCreated(DocumentStream documentStream) {
		log.debug("Documento creade desde un stream de datos");
	}

	public void documentCreated(DocumentFile documentFile) {
		File file = documentFile.getFile();
        boolean deleted = file.delete();
       	if (deleted) {
       		log.info("Documento eliminado");
       		try {
				removeFolderIfProcessed(file.getParentFile());
			} catch (IOException e) {
				log.error("Error eliminando carpeta", e);
			}
        } else {
        	log.error(String.format("File could not be deleted: %s", documentFile.getOriginalFilename()));
        }
		
	}

	public void notifyUpload(SourceDocument sourceDocument) {
		sourceDocument.documentCreated(this);
	}
	
	private boolean removeFolderIfProcessed(File folder) throws IOException {
		String cannonicalPath = folder.getCanonicalPath();
		try {
			validatePath(cannonicalPath);
		} catch (InvalidFolderException e) {
			log.error("El archivo no es valido: " + cannonicalPath);
			return false;
		}
		if (!folder.isDirectory()) {
			log.error("El archivo no es una carpeta: " + cannonicalPath);
			return false;
		} 
		if (isRootDirectory(cannonicalPath)) {
			return false;
		}
		if (!isProcessed(folder)) {
			return false;
		}
		
		log.info("Carpeta procesada, eliminando: " + cannonicalPath);
		boolean deleted = FileUtils.deleteQuietly(folder);
		if (deleted) {
			log.debug("La carpeta fue eliminada: " + cannonicalPath + ". Continuando con la carpeta padre");
			removeFolderIfProcessed(folder.getParentFile());
			return true;
		} else {
			log.error("La carpeta no pudo eliminarse: " + cannonicalPath);
			return false;
		}

	}
	
	private boolean isProcessed(File folder) throws IOException {	
		return countDocuments(0, folder) == 0;
	}
	
}
